package edu.nf.takeoutdemo.commons.utils;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * JWT 工具类 —— JJWT 0.12.5 零废弃 API
 * 1. 生成
 * 2. 解析
 * 3. 刷新
 * 4. 线程安全
 */
public final class JwtUtil {
    /**
     * 生成 JWT
     * @param secretKey  密钥字符串（长度≥32）
     * @param ttlMillis  有效期（毫秒）
     * @param claims     自定义声明
     */
    public static String createJWT(String secretKey,
                                   long ttlMillis,
                                   Map<String, Object> claims) {

        SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
        Date now = new Date();
        Date exp = new Date(now.getTime() + ttlMillis);

        return Jwts.builder()
                .claims(claims)          // 自定义声明
//                .issuedAt(now)           // iat
//                .expiration(exp)         // exp
                .signWith(key, Jwts.SIG.HS256) // 0.12.x 新签名方式
                .compact();
    }

    /* ==========================  解析  ========================== */

    /**
     * 解析 JWT
     * @param secretKey 密钥
     * @param token     令牌
     * @return Claims
     * @throws JwtException 令牌非法、过期、签名不一致
     */
    public static Claims parseJWT(String secretKey, String token) throws JwtException {
        SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));

        return Jwts.parser()
                .verifyWith(key)          // 0.12.x 新验证方式
                .build()
                .parseSignedClaims(token) // 0.12.x 新解析方式
                .getPayload();
    }

    /* ==========================  刷新  ========================== */

    /**
     * 用旧令牌里的声明重新签发新令牌（沿用默认 TTL）
     */
    public static String refresh(String oldToken,
                                 String secretKey,
                                 long ttlMillis) {
        Claims claims = parseJWT(secretKey, oldToken);
        claims.remove(Claims.ISSUED_AT);
        claims.remove(Claims.EXPIRATION);
        return createJWT(secretKey, ttlMillis, claims);
    }

    /* ==========================  快捷方法  ========================== */

    public static boolean validate(String secretKey, String token) {
        try {
            parseJWT(secretKey, token);
            return true;
        } catch (JwtException e) {
            return false;
        }
    }
}
